<template>
  <div class="comp-column-selector">
    <div class="ant-checkbox-group col-box" @drop="onDrop">
      <div class="col-item" :draggable="false">
        <label
          class="ant-checkbox-wrapper ant-checkbox-group-item"
          :class="{
            'ant-checkbox-wrapper-checked': isSelectAll,
          }"
          @click="selectAll"
        >
          <span
            class="ant-checkbox"
            :class="{
              'ant-checkbox-checked': isSelectAll,
            }"
          >
            <span class="ant-checkbox-inner"></span>
          </span>
          <span>全选</span>
        </label>
      </div>
      <div
        v-for="(op, i) in optionsRender"
        :key="op.value"
        class="col-item"
        :draggable="true"
        @dragstart="
          ($event) => {
            onDragStart($event, op, i)
          }
        "
        @dragend="onDragEnd"
        @dragover.stop="onDragOver($event, op, i)"
        @dragenter="onDragEnter($event, op, i)"
        @dragleave="onDragLeave"
      >
        <label
          class="ant-checkbox-wrapper ant-checkbox-group-item"
          :class="{
            'ant-checkbox-wrapper-checked': value.indexOf(op.value) > -1,
            'ant-checkbox-wrapper-disabled': op.disabled,
          }"
          @click="onOptionClicked(op, value.includes(op.value))"
        >
          <span
            class="ant-checkbox"
            :class="{
              'ant-checkbox-checked': value.indexOf(op.value) > -1,
            }"
          >
            <span class="ant-checkbox-inner"></span>
          </span>
          <span>{{ op.label }}</span>
        </label>
        <span class="drag-icon" :title="'拖动改变顺序'"></span>
      </div>
    </div>
  </div>
</template>
<script lang="ts">
/*
 * 列选择器，带顺序切换功能
 */

import { cloneDeep, isEqual } from 'lodash'
import Vue from 'vue'
import KV from './kv'

export default Vue.extend({
  props: {
    value: {
      type: Array,
      default() {
        return []
      },
    },
    options: {
      type: Array,
      default() {
        return []
      },
    },
  },
  data() {
    const defaultValue: KV = {
      dragIndex: -1,
      enterIndex: -1,
      enterTarget: null,
      dragTarget: null,
      dragOverTarget: null,
      optionsRender: [],
    }
    return defaultValue
  },
  computed: {
    isSelectAll() {
      // 是否全选
      return isEqual(
        this.value,
        // @ts-ignore
        this.optionsRender.map((option: any) => option.value)
      )
    },
  },
  watch: {
    options: {
      immediate: true,
      deep: true,
      handler(newValue_: Array<any>) {
        const newValue = cloneDeep(newValue_)
        if (this.value && this.value.length > 0) {
          newValue.forEach((option: KV, index: number) => {
            if (this.value.includes(option.value)) {
              newValue[index].$$$index = this.value.indexOf(option.value)
            } else {
              newValue[index].$$$index = 10000 + index
            }
          })
          newValue.sort((a, b) => {
            return a.$$$index - b.$$$index
          })
        }

        this.optionsRender = newValue
      },
    },
  },
  methods: {
    onDragOver($event: any, op: KV, index: number) {
      const target = $event.target as HTMLElement
      if (target && target.classList.contains('col-item')) {
        target.style.border = '1px solid #C5C9FF'
        this.dragOverTarget = target
      }
      if ($event.preventDefault) {
        $event.preventDefault()
      }
      $event.dataTransfer.dropEffect = 'move'
      this.enterIndex = index
      return false
    },
    onDrop() {
      if (this.dragIndex >= 0 && this.enterIndex >= 0) {
        const optionsNew = cloneDeep(this.optionsRender) as Array<any>
        const oldOption = cloneDeep(optionsNew[this.dragIndex])
        if (this.dragIndex !== this.enterIndex) {
          optionsNew.splice(
            this.enterIndex + (this.dragIndex < this.enterIndex ? 1 : 0),
            0,
            oldOption
          )
          optionsNew.splice(
            this.dragIndex + (this.dragIndex < this.enterIndex ? 0 : 1),
            1
          )
          this.optionsRender = optionsNew
          const valueNew = [] as Array<string>
          ;(this.optionsRender as Array<any>).forEach((option: KV) => {
            if (this.value.includes(option.value)) {
              valueNew.push(option.value)
            }
          })
          this.$emit('input', valueNew)
          this.$emit('change', valueNew)
          this.$emit('optiondragend', optionsNew)
        }
      }
      if (this.enterTarget) {
        ;(this.enterTarget as HTMLElement).style.border = 'none'
      }
      if (this.dragOverTarget) {
        ;(this.dragOverTarget as HTMLElement).style.border = 'none'
      }
    },
    onDragEnd() {
      if (this.dragTarget) {
        this.dragTarget.style.opacity = '1'
      }
    },
    onDragStart($event: any, op: KV, index: number) {
      $event.dataTransfer.setData('dragname', 'column')
      this.dragTarget = $event.target as HTMLElement
      this.dragTarget.style.opacity = '0.6'
      this.dragIndex = index
    },
    onDragEnter($event: any, op: KV, index: number) {
      this.enterIndex = index
    },
    onDragLeave($event: any) {
      if ($event.target) {
        ;($event.target as HTMLElement).style.border = 'none'
        this.enterIndex = -1
      }
    },
    onOptionClicked(op: KV, isChecked: boolean) {
      if (!op.disabled) {
        if (isChecked) {
          const newValue = this.value.filter((item) => item !== op.value)
          this.$emit('input', newValue)
          this.$nextTick(() => {
            this.$emit('change', newValue)
          })
        } else {
          const newValue = cloneDeep(this.value)
          newValue.push(op.value)
          const valueNew = [] as Array<string>
          ;(this.optionsRender as Array<any>).forEach((option: KV) => {
            if (newValue.includes(option.value)) {
              valueNew.push(option.value)
            }
          })
          this.$emit('input', valueNew)
          this.$nextTick(() => {
            this.$emit('change', valueNew)
          })
        }
      }
    },
    selectAll() {
      if (this.isSelectAll) {
        // 全选状态下
        this.$emit('change', [])
      } else {
        this.$emit(
          'change',
          this.optionsRender.map((option: any) => option.value)
        )
      }
    },
  },
})
</script>
<style lang="less" scoped>
.comp-column-selector {
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid #eaeaea;
  border-radius: 2px;
  display: block;
  padding: 8px 0;
}

.col-box {
  display: block;
}

.col-item {
  align-items: center;
  display: flex;
  flex-flow: row;
  flex-wrap: nowrap;
  height: 36px;
  justify-content: space-between;
  padding: 0 16px;
  width: 100%;

  .drag-icon {
    background: url('~@/assets/vis-controller/sort.png') no-repeat;
    background-size: 24px auto;
    cursor: ns-resize;
    display: none;
    height: 28px;
    width: 28px;
  }

  &:hover {
    background: rgba(236, 238, 253, 1);

    .drag-icon {
      display: block;
    }
  }

  .ant-checkbox-wrapper {
    color: #373b52;
    font-size: 14px;
    font-weight: 400;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    width: calc(100% - 40px);
  }
}
</style>
